home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_87 / soundbla.pas < prev    next >
Pascal/Delphi Source File  |  1995-01-01  |  27KB  |  910 lines

  1. {****************************************************************************}
  2. {                                                                            }
  3. { MODULE:         SoundBlaster                                               }
  4. {                                                                            }
  5. { DESCRIPTION:    An UNIT that provides several routines for handling the    }
  6. {                 Sound Blaster and Sound Blaster Pro cards and compatibles. }
  7. {                                                                            }
  8. { AUTHOR:         Juan Carlos Arévalo                                        }
  9. {                                                                            }
  10. { MODIFICATIONS:  Nobody (yet ;-)                                            }
  11. {                                                                            }
  12. { HISTORY:        12-Nov-1992 Documentation.                                 }
  13. {                 26-Nov-1992 Included SB 16 support.                        }
  14. {                                                                            }
  15. { (C) 1992 VangeliSTeam                                                      }
  16. {____________________________________________________________________________}
  17.  
  18. UNIT SoundBlaster;
  19.  
  20. {$R-}
  21.  
  22. INTERFACE
  23.  
  24. USES SoundDevices;
  25.  
  26.  
  27.  
  28.  
  29. { I/O Port offsets. }
  30.  
  31. CONST
  32.   CMS1DataPortOffset = $00;  { CM/S 1-6  Data port.             Write Only. }
  33.   CMS1AddrPortOffset = $01;  { CM/S 1-6  Address port.          Write Only. }
  34.   CMS2DataPortOffset = $02;  { CM/S 7-12 Data port.             Write Only. }
  35.   CMS2AddrPortOffset = $03;  { CM/S 7-12 Address port.          Write Only. }
  36.  
  37.   MixAddrPortOffset  = $04;  { Mixer register port.             Write Only. }
  38.   MixDataPortOffset  = $05;  { Mixer data port.                 Read/Write. }
  39.  
  40.   FMStatPortOffset   = $08;  { Mono FM Status port.             Read  Only. }
  41.   FMAddrPortOffset   = $08;  { Mono FM Address port.            Write Only. }
  42.   FMDataPortOffset   = $09;  { Mono FM Data port.               Write Only. }
  43.  
  44.   LFMStatPortOffset  = $00;  { Left FM Status port.             Read  Only. }
  45.   LFMAddrPortOffset  = $00;  { Left FM Address port.            Write Only. }
  46.   LFMDataPortOffset  = $01;  { Left FM Data port.               Write Only. }
  47.  
  48.   RFMStatPortOffset  = $02;  { Right FM Status port.            Read  Only. }
  49.   RFMAddrPortOffset  = $02;  { Right FM Address port.           Write Only. }
  50.   RFMDataPortOffset  = $03;  { Right FM Data port.              Write Only. }
  51.  
  52.   DSPResetPortOffset = $06;  { DSP Reset port.                  Write Only. }
  53.   DSPReadPortOffset  = $0A;  { DSP Read data port.              Read  Only. }
  54.   DSPLifePortOffset  = $0A;  { DSP Read data port.              Read  Only. }
  55.   DSPWStatPortOffset = $0C;  { DSP Write buffer status port.    Write Only. }
  56.   DSPWritePortOffset = $0C;  { DSP Write data port.             Write Only. }
  57.   DSPRStatPortOffset = $0E;  { DSP Read buffer status port.     Read  Only. }
  58.   DSP8AckPortOffset  = $0E;  {  8 bit DMA IRQ Acknowledge port. Write Only. }
  59.   DSP16AckPortOffset = $0F;  { 16 bit DMA IRQ Acknowledge port. Write Only. }
  60.  
  61.   CDDataPortOffset   = $10;  { CD-ROM Data port.                Read  Only. }
  62.   CDCmdPortOffset    = $10;  { CD-ROM Command port.             Write Only. }
  63.   CDStatPortOffset   = $11;  { CD-ROM Status port.              Read  Only. }
  64.   CDResetPortOffset  = $12;  { CD-ROM Reset port.               Write Only. }
  65.   CDEnablePortOffset = $13;  { CD-ROM Enable port.              Write Only. }
  66.  
  67.  
  68. { I/O Ports. Same as above. }
  69.  
  70. CONST
  71.   CMS1DataPort : WORD    = $220 + CMS1DataPortOffset;
  72.   CMS1AddrPort : WORD    = $220 + CMS1AddrPortOffset;
  73.   CMS2DataPort : WORD    = $220 + CMS2DataPortOffset;
  74.   CMS2AddrPort : WORD    = $220 + CMS2AddrPortOffset;
  75.  
  76.   MixAddrPort  : WORD    = $220 + MixAddrPortOffset;
  77.   MixDataPort  : WORD    = $220 + MixDataPortOffset;
  78.  
  79.   FMStatPort   : WORD    = $220 + FMStatPortOffset;
  80.   FMAddrPort   : WORD    = $220 + FMAddrPortOffset;
  81.   FMDataPort   : WORD    = $220 + FMDataPortOffset;
  82.  
  83.   LFMStatPort  : WORD    = $220 + LFMStatPortOffset;
  84.   LFMAddrPort  : WORD    = $220 + LFMAddrPortOffset;
  85.   LFMDataPort  : WORD    = $220 + LFMDataPortOffset;
  86.  
  87.   RFMStatPort  : WORD    = $220 + RFMStatPortOffset;
  88.   RFMAddrPort  : WORD    = $220 + RFMAddrPortOffset;
  89.   RFMDataPort  : WORD    = $220 + RFMDataPortOffset;
  90.  
  91.   DSPResetPort : WORD    = $220 + DSPResetPortOffset;
  92.   DSPReadPort  : WORD    = $220 + DSPReadPortOffset;
  93.   DSPLifePort  : WORD    = $220 + DSPLifePortOffset;
  94.   DSPWStatPort : WORD    = $220 + DSPWStatPortOffset;
  95.   DSPWritePort : WORD    = $220 + DSPWritePortOffset;
  96.   DSPRStatPort : WORD    = $220 + DSPRStatPortOffset;
  97.   DSP8AckPort  : WORD    = $220 + DSP8AckPortOffset;
  98.   DSP16AckPort : WORD    = $220 + DSP16AckPortOffset;
  99.  
  100.   CDDataPort   : WORD    = $220 + CDDataPortOffset;
  101.   CDCmdPort    : WORD    = $220 + CDCmdPortOffset;
  102.   CDStatPort   : WORD    = $220 + CDStatPortOffset;
  103.   CDResetPort  : WORD    = $220 + CDResetPortOffset;
  104.   CDEnablePort : WORD    = $220 + CDEnablePortOffset;
  105.  
  106.  
  107. { Configuration. }
  108.  
  109. CONST
  110.   SbPort       : WORD    = $FFFF; { Base port. $FFFF Means Autodetect.      }
  111.   SbIrq        : WORD    = 7;     { DMA IRQ level.                          }
  112.   SbDMAChan    : WORD    = 1;     { DMA channel.                            }
  113.   SbDefTimeout : WORD    = 5000;  { Default DSP timeout.                    }
  114.   SbHiSpeed    : BOOLEAN = TRUE;  { User Desires HS DMA mode if TRUE.       }
  115.   SbForce      : BOOLEAN = FALSE; { Force TRUE the detection of the SB.     }
  116.   MixerForce   : BOOLEAN = FALSE; { Force TRUE the detection of the Mixer.  }
  117.   SbProForce   : BOOLEAN = FALSE; { Force TRUE the detection of the SB Pro. }
  118.   Sb16Force    : BOOLEAN = FALSE; { Force TRUE the detection of the SB 16.  }
  119.  
  120.  
  121. { Card information. }
  122.  
  123. CONST
  124.   SbVersionMin : BYTE       = 0;
  125.   SbVersionMaj : BYTE       = 0;
  126.   SbVersionStr : STRING[ 5] = '';
  127.   SbCopyright  : STRING[80] = '';
  128.   SbResponse1  : BYTE       = 0;
  129.   SbResponse2  : BYTE       = 0;
  130.  
  131. VAR
  132.   SbVersion    : WORD    ABSOLUTE SbVersionMin;
  133.  
  134.  
  135. { Run-time information. }
  136.  
  137. CONST
  138.   SbRegDetected     : BOOLEAN = FALSE;
  139.   SbRegInited       : BOOLEAN = FALSE;
  140.   SbProDetected     : BOOLEAN = FALSE;
  141.   SbProInited       : BOOLEAN = FALSE;
  142.   Sb16Detected      : BOOLEAN = FALSE;
  143.   Sb16Inited        : BOOLEAN = FALSE;
  144.   MixerDetected     : BOOLEAN = FALSE;
  145.  
  146.   SbWorksOk         : BOOLEAN = TRUE;  { Set to FALSE if DSP timeouts.         }
  147.   HSBlockSpecified  : WORD    = 0;     { Set to the last hi-speed block size.  }
  148.   Sb16BlockSpecified: WORD    = 0;     { Set to the last Sb 16 block size.     }
  149.   SbStereo          : BOOLEAN = FALSE; { Stereo DMA mode if TRUE.              }
  150.   SbFilter          : BOOLEAN = FALSE; { SB Pro output filter ON if TRUE.      }
  151.  
  152.   DoHiSpeed         : BOOLEAN = FALSE; { Hi speed DMA mode if TRUE.            }
  153.   Sb16Bit           : BOOLEAN = FALSE; { 16 bit output if TRUE.                }
  154.  
  155.   TimeConst         : BYTE    = 0;
  156.  
  157.   DMAStart          : BOOLEAN = FALSE;
  158.   DMAStop           : BOOLEAN = FALSE;
  159.   DMAStopped        : BOOLEAN = FALSE;
  160.  
  161.   DMAIrqWatch       : BYTE    = 0;
  162.  
  163.  
  164.  
  165. { DSP Commands. }
  166.  
  167. CONST
  168.   sdcSendOneSample  = $10;  { Send a sample to the DAC directly (mono mode only). }
  169.   sdcStartLSpeedDMA = $14;  { Start a low-speed DMA transfer.                     }
  170.   sdcSetTimeConst   = $40;  { Set the time constant.                              }
  171.   sdcSetHSpeedSize  = $48;  { Set hi-speed DMA transfer length.                   }
  172.   sdcStartHSpeedDMA = $91;  { Start a hi-speed DMA transfer.                      }
  173.   sdcTurnOnSpeaker  = $D1;  { Turn on the SB speaker.                             }
  174.   sdcTurnOffSpeaker = $D3;  { Turn off the SB speaker.                            }
  175.   sdcGetDSPVersion  = $E1;  { Get the DSP version number.                         }
  176.   sdcGetCopyright   = $E3;  { Get the card copyright string.                      }
  177.  
  178.  
  179. { Mixer registers. }
  180.  
  181. CONST
  182.   mxrDataReset    = $00;
  183.   mxrDACVolume    = $04;
  184.   mxrMicMixing    = $0A;
  185.   mxrInSetting    = $0C;
  186.   mxrOutSetting   = $0E;
  187.   mxrMasterVolume = $22;
  188.   mxrFMVolume     = $26;
  189.   mxrCDVolume     = $28;
  190.   mxrLineVolume   = $2E;
  191.  
  192.  
  193. { Bit masks for the mixer registers. }
  194.  
  195. CONST
  196.   mxiFilterVal = $38;
  197.   mxiADCVal    = $06;
  198.   mxoFilterNeg = $20;
  199.   mxoStereoOn  = $02;
  200.  
  201. TYPE
  202.   TMixerVolume = (mvMaster,
  203.                   mvVoice,
  204.                   mvFM,
  205.                   mvLine,
  206.                   mvMicrophone,
  207.                   mvSpeaker,
  208.                   mvCD);
  209.  
  210. CONST
  211.   SbProRegs : ARRAY[mvMaster..mvCD] OF BYTE = ( $22, $04, $26, $2E, $0A, $00, $28 );
  212.   Sb16Regs  : ARRAY[mvMaster..mvCD] OF BYTE = ( $30, $32, $34, $38, $3A, $3B, $34 );
  213.  
  214.  
  215.  
  216.  
  217. { SB basic }
  218.  
  219. FUNCTION  SbReset                                       : BOOLEAN;
  220.  
  221. PROCEDURE SbWriteLoop    (t: WORD);
  222. PROCEDURE SbWriteByte    (t: WORD; b: BYTE);
  223. PROCEDURE SbReadLoop     (t: WORD);
  224. FUNCTION  SbReadByte     (t: WORD)                      : BYTE;
  225.  
  226.  
  227. { Mixer basic }
  228.  
  229. PROCEDURE SbWriteMixerReg (Reg, Val: BYTE);
  230. FUNCTION  SbReadMixerReg  (Reg: BYTE)                    : BYTE;
  231.  
  232.  
  233. { SB Reg }
  234.  
  235. FUNCTION  SbRegDetect : BOOLEAN;
  236. PROCEDURE SbRegInit;
  237. PROCEDURE SbRegDone;
  238.  
  239. PROCEDURE SbGetDSPVersion;
  240. PROCEDURE SbGetCopyrightString;
  241. PROCEDURE SbSetTimeConst    (tc: BYTE);
  242. PROCEDURE SbUpdateTimeConst;
  243. PROCEDURE SbStartSampleLS   (Len: WORD; Cont: BOOLEAN);
  244. PROCEDURE SbStartSampleHS   (Len: WORD; Cont: BOOLEAN);
  245. PROCEDURE SbPlaySample      (Len: WORD; Cont: BOOLEAN);
  246.  
  247.  
  248. { Mixer }
  249.  
  250. FUNCTION MixerDetect : BOOLEAN;
  251.  
  252. PROCEDURE MixerSetVolume(Reg: TMixerVolume;     VolLeft, VolRight: BYTE);
  253. FUNCTION  MixerGetVolume(Reg: TMixerVolume; VAR VolLeft, VolRight: BYTE) : BOOLEAN;
  254.  
  255.  
  256. { SB Pro }
  257.  
  258. FUNCTION  SbProDetect : BOOLEAN;
  259. PROCEDURE SbProInit;
  260. PROCEDURE SbProDone;
  261.  
  262. PROCEDURE SbProSetStereo (Stereo: BOOLEAN);
  263. PROCEDURE SbProSetFilter (Filter: BOOLEAN);
  264.  
  265.  
  266. { SB 16 }
  267.  
  268. FUNCTION  Sb16Detect : BOOLEAN;
  269. PROCEDURE Sb16Init;
  270. PROCEDURE Sb16Done;
  271.  
  272. PROCEDURE Sb16StartSample(Len: WORD; Cont: BOOLEAN);
  273.  
  274.  
  275.  
  276.  
  277. IMPLEMENTATION
  278.  
  279. USES Debugging;
  280.  
  281.  
  282.  
  283. {----------------------------------------------------------------------------}
  284. { Sound Blaster basic routines.                                              }
  285. {____________________________________________________________________________}
  286.  
  287. FUNCTION SbReset : BOOLEAN;
  288.   CONST
  289.     ready = $AA;
  290.   VAR
  291.     ct, stat : BYTE;
  292.   BEGIN
  293.     PORT[DSPResetPort] := 1;
  294.     FOR ct := 1 TO 100 DO;
  295.     PORT[DSPResetPort] := 0;
  296.  
  297.     stat := 0;
  298.     ct   := 0;
  299.     WHILE (stat <> ready) AND (ct < 100) DO BEGIN
  300.       stat := PORT[DSPRStatPort];
  301.       stat := PORT[DSPReadPort];
  302.       INC(ct);
  303.     END;
  304.  
  305.     SbReset := stat = ready;
  306.   END;
  307.  
  308.  
  309. PROCEDURE SbWriteLoop(t: WORD); ASSEMBLER;
  310.   ASM
  311.  
  312.                 MOV     BX,t
  313.                 MOV     DX,[DSPWritePort]
  314. @@lp:            DEC    BX
  315.                  JZ     @@fin
  316.                  IN     AL,DX
  317.                  ADD    AL,AL
  318.                  JC     @@lp
  319. @@fin:          OR      BL,BH
  320.                 MOV     [SbWorksOk],BL
  321.   END;
  322.  
  323.  
  324. PROCEDURE SbWriteByte(t: WORD; b: BYTE); ASSEMBLER;
  325.   ASM
  326.  
  327.                 MOV     AL,b
  328.                 XOR     AH,AH
  329.                 PUSH    AX
  330.                 PUSH    $60
  331.                 CALL    WriteSNum
  332.  
  333.                 MOV     AX,t
  334.                 PUSH    AX
  335.                 CALL    SbWriteLoop
  336.                 JNZ     @@ya
  337.  
  338.                 MOV     DX,[DSPLifePort]
  339.                 IN      AL,DX
  340.  
  341.                 MOV     AX,t
  342.                 PUSH    AX
  343.                 CALL    SbWriteLoop
  344.  
  345. @@ya:           MOV     AL,b
  346.                 OUT     DX,AL
  347.  
  348.                 MOV     AL,[SbWorksOk]
  349.                 ADD     AL,'A'
  350.                 XOR     AH,AH
  351.                 PUSH    AX
  352.                 PUSH    $40
  353.                 CALL    WriteChar
  354.  
  355.   END;
  356.  
  357.  
  358. PROCEDURE SbReadLoop(t: WORD); ASSEMBLER;
  359.   ASM
  360.  
  361.                 MOV     BX,t
  362.                 MOV     DX,[DSPRStatPort]
  363. @@lp:            DEC    BX
  364.                  JZ     @@fin
  365.                  IN     AL,DX
  366.                  ADD    AL,AL
  367.                  JNC    @@lp
  368. @@fin:          OR      BL,BH
  369.                 MOV     [SbWorksOk],BL
  370.                 MOV     DX,[DSPReadPort]
  371.   END;
  372.  
  373.  
  374. FUNCTION SbReadByte(t: WORD) : BYTE; ASSEMBLER;
  375.   ASM
  376.                 MOV     AX,t
  377.                 PUSH    AX
  378.                 CALL    SbReadLoop
  379.                 JNZ     @@ya
  380. {
  381.                 MOV     DX,[DSPLifePort]
  382.                 IN      AL,DX
  383.  
  384.                 MOV     AX,t
  385.                 PUSH    AX
  386.                 CALL    SbReadLoop
  387. }
  388. @@ya:           IN      AL,DX
  389.   END;
  390.  
  391.  
  392.  
  393.  
  394. {----------------------------------------------------------------------------}
  395. { Mixer basic routines.                                                      }
  396. {____________________________________________________________________________}
  397.  
  398. PROCEDURE SbWriteMixerReg(Reg, Val: BYTE); ASSEMBLER;
  399.   ASM
  400.  
  401.                 MOV     DX,[MixAddrPort]
  402.                 MOV     AL,[Reg]
  403.                 OUT     DX,AL
  404.  
  405.                 MOV     DX,[MixDataPort]
  406.                 MOV     AL,[Val]
  407.                 OUT     DX,AL
  408.  
  409.   END;
  410.  
  411.  
  412. FUNCTION SbReadMixerReg(Reg: BYTE) : BYTE; ASSEMBLER;
  413.   ASM
  414.  
  415.                 MOV     DX,[MixAddrPort]
  416.                 MOV     AL,[Reg]
  417.                 OUT     DX,AL
  418.  
  419.                 MOV     DX,[MixDataPort]
  420.                 IN      AL,DX
  421.  
  422.   END;
  423.  
  424.  
  425.  
  426.  
  427. {----------------------------------------------------------------------------}
  428. { Regular Sound Blaster generic routines.                                    }
  429. {____________________________________________________________________________}
  430.  
  431. FUNCTION SbRegDetect : BOOLEAN;
  432.   VAR
  433.     Port, Lst : WORD;
  434.   BEGIN
  435.  
  436.     SbRegDetect := SbRegDetected;
  437.  
  438.     IF SbRegDetected THEN EXIT;
  439.  
  440.     IF SbPort < $8000 THEN
  441.       BEGIN
  442.         Port := SbPort;
  443.         Lst  := SbPort;
  444.       END
  445.     ELSE
  446.       BEGIN
  447.         Port := $210;
  448.         Lst  := $280;
  449.       END;
  450.  
  451.     WHILE (NOT SbRegDetected) AND (Port <= Lst) DO BEGIN
  452.       CMS1DataPort := Port + CMS1DataPortOffset;
  453.       CMS1AddrPort := Port + CMS1AddrPortOffset;
  454.       CMS2DataPort := Port + CMS2DataPortOffset;
  455.       CMS2AddrPort := Port + CMS2AddrPortOffset;
  456.  
  457.       MixAddrPort  := Port + MixAddrPortOffset;
  458.       MixDataPort  := Port + MixDataPortOffset;
  459.  
  460.       FMStatPort   := Port + FMStatPortOffset;
  461.       FMAddrPort   := Port + FMAddrPortOffset;
  462.       FMDataPort   := Port + FMDataPortOffset;
  463.  
  464.       LFMStatPort  := Port + LFMStatPortOffset;
  465.       LFMAddrPort  := Port + LFMAddrPortOffset;
  466.       LFMDataPort  := Port + LFMDataPortOffset;
  467.  
  468.       RFMStatPort  := Port + RFMStatPortOffset;
  469.       RFMAddrPort  := Port + RFMAddrPortOffset;
  470.       RFMDataPort  := Port + RFMDataPortOffset;
  471.  
  472.       DSPResetPort := Port + DSPResetPortOffset;
  473.       DSPReadPort  := Port + DSPReadPortOffset;
  474.       DSPLifePort  := Port + DSPLifePortOffset;
  475.       DSPWStatPort := Port + DSPWStatPortOffset;
  476.       DSPWritePort := Port + DSPWritePortOffset;
  477.       DSPRStatPort := Port + DSPRStatPortOffset;
  478.       DSP8AckPort  := Port + DSP8AckPortOffset;
  479.       DSP16AckPort := Port + DSP16AckPortOffset;
  480.  
  481.       CDDataPort   := Port + CDDataPortOffset;
  482.       CDCmdPort    := Port + CDCmdPortOffset;
  483.       CDStatPort   := Port + CDStatPortOffset;
  484.       CDResetPort  := Port + CDResetPortOffset;
  485.       CDEnablePort := Port + CDEnablePortOffset;
  486.  
  487.       SbRegDetected := SbReset;
  488.  
  489.       IF NOT SbRegDetected THEN INC(Port, $10);
  490.     END;
  491.  
  492.     SbRegDetect := SbRegDetected;
  493.  
  494.   END;
  495.  
  496.  
  497. PROCEDURE SbRegInit;
  498.   BEGIN
  499.  
  500.     IF NOT SbRegDetect THEN EXIT;
  501.  
  502.     IF NOT SbRegInited THEN
  503.       BEGIN
  504. (*
  505.         SbWriteByte(SbDefTimeout, $E0);
  506.         SbWriteByte(SbDefTimeout, $AA);
  507.         SbResponse1 := SbReadByte (SbDefTimeout); { $55 }
  508.         SbWriteByte(SbDefTimeout, $E4);
  509.         SbWriteByte(SbDefTimeout, $AA);
  510.         SbWriteByte(SbDefTimeout, $E8);
  511.         SbResponse2 := SbReadByte (SbDefTimeout); { $AA }
  512. *)
  513.         SbGetDSPVersion;
  514.  
  515.         DoHiSpeed := (SbVersion >= $200) AND SbHiSpeed {AND FALSE};
  516. {
  517.         IF DoHiSpeed THEN
  518.           BEGIN
  519.             SbWriteByte(SbDefTimeout, $48);
  520.             SbWriteByte(SbDefTimeout, $00);
  521.             SbWriteByte(SbDefTimeout, $00);
  522.             SbWriteByte(SbDefTimeout, $91);
  523.           END;
  524. }
  525.         SbWriteByte(SbDefTimeout, sdcTurnOnSpeaker);
  526.  
  527.       END;
  528.  
  529.     SbRegInited := TRUE;
  530.  
  531.   END;
  532.  
  533.  
  534. PROCEDURE SbRegDone;
  535.   BEGIN
  536.     IF NOT (SbRegDetected AND SbRegInited) THEN EXIT;
  537.     SbWriteByte(SbDefTimeout, sdcTurnOffSpeaker);
  538.   END;
  539.  
  540.  
  541.  
  542.  
  543. PROCEDURE SbGetDSPVersion;
  544.   VAR
  545.     i : WORD;
  546.     t : WORD;
  547.     s : STRING[2];
  548.   BEGIN
  549.     SbWriteByte(SbDefTimeout, sdcGetDSPVersion); { Send command. }
  550.     t := 0;
  551.     REPEAT
  552.       SbVersionMaj := SbReadByte($FFFF);
  553.       INC(t);
  554.     UNTIL ((SbVersionMaj <> $AA) AND SbWorksOk) OR (t >= 10);
  555.     SbVersionMin := SbReadByte(SbDefTimeout);
  556.  
  557.     STR(SbVersionMaj, SbVersionStr);
  558.     SbVersionStr := SbVersionStr + '.';
  559.     STR(SbVersionMin, s);
  560.     IF SbVersionMin > 9 THEN SbVersionStr := SbVersionStr +       s
  561.                         ELSE SbVersionStr := SbVersionStr + '0' + s;
  562.   END;
  563.  
  564.  
  565. PROCEDURE SbGetCopyrightString;
  566.   VAR
  567.     t : WORD;
  568.   BEGIN
  569.     SbWriteByte(SbDefTimeout, sdcGetCopyright); { Send command. }
  570.     t := 0;
  571.     REPEAT
  572.       SbCopyright := CHAR(SbReadByte($FFFF));
  573.       INC(t);
  574.     UNTIL ((SbCopyright[1] <> #$AA) AND SbWorksOk) OR (t = 10);
  575.  
  576.     WHILE SbWorksOk AND (Length(SbCopyright) < 80) DO
  577.       SbCopyright := SbCopyright + CHAR(SbReadByte(SbDefTimeout));
  578.  
  579.     DEC(SbCopyright[0]);
  580.   END;
  581.  
  582.  
  583. PROCEDURE SbSetTimeConst(tc: BYTE);
  584.   BEGIN
  585. WriteChar('T', $04);
  586.     IF Sb16Detected THEN
  587.       BEGIN
  588. WriteChar('|', $04);
  589.         IF Sb16Bit THEN
  590.           SbWriteByte(SbDefTimeout, $D9)  { Send time constant command.             }
  591.         ELSE
  592.           SbWriteByte(SbDefTimeout, $DA); { Send time constant command.             }
  593.       END;
  594.     SbWriteByte(SbDefTimeout,   sdcSetTimeConst); { Send time constant command.             }
  595.     SbWriteByte(SbDefTimeout*4, tc);              { Send the time constant.                 }
  596.     TimeConst := 0;                               { Reset time constant to already changed. }
  597.     IF Sb16Detected THEN
  598.       IF Sb16Bit THEN
  599.         SbWriteByte(SbDefTimeout, $47)  { Send time constant command.             }
  600.       ELSE
  601.         SbWriteByte(SbDefTimeout, $45); { Send time constant command.             }
  602.   END;
  603.  
  604.  
  605. PROCEDURE SbUpdateTimeConst;
  606.   BEGIN
  607.     IF TimeConst = 0 THEN EXIT;                 { If not changed then do nothing.         }
  608.     SbSetTimeConst(TimeConst);
  609.   END;
  610.  
  611.  
  612. PROCEDURE SbStartSampleLS(Len: WORD; Cont: BOOLEAN);
  613.   BEGIN
  614.     HSBlockSpecified := 0;   { Reset Hi-speed block specifier, just in case. }
  615.  
  616. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  617.     SbWriteByte(SbDefTimeout, sdcStartLSpeedDMA); { Start DMA low speed command.   }
  618. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  619.     SbWriteByte(SbDefTimeout, LO(Len));           { Low & high bytes of size.      }
  620. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  621.     SbWriteByte(SbDefTimeout, HI(Len));
  622.  
  623. WriteSNum(Len, $30);
  624. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  625.  
  626.   END;
  627.  
  628.  
  629. PROCEDURE SbStartSampleHS(Len: WORD; Cont: BOOLEAN);
  630.   BEGIN
  631. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  632.     IF HSBlockSpecified <> Len THEN Cont := FALSE;
  633.     IF NOT Cont THEN
  634.       BEGIN
  635.         SbWriteByte(SbDefTimeout, sdcSetHSpeedSize);  { Set hi speed DMA block command. }
  636. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  637.         SbWriteByte(SbDefTimeout, LO(Len));           { Low & high bytes of size.       }
  638. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  639.         SbWriteByte(SbDefTimeout, HI(Len));
  640. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  641.         HSBlockSpecified := Len;
  642.  
  643.         WriteSNum(Len, $30);
  644.  
  645.       END;
  646.  
  647.     IF NOT (Sb16Detected AND Cont) THEN
  648.       SbWriteByte(SbDefTimeout, sdcStartHSpeedDMA); { Start DMA in hi speed mode.    }
  649. {WriteChar(CHAR(BYTE(SbWorksOk) + BYTE('A')), $40);}
  650.   END;
  651.  
  652.  
  653. PROCEDURE SbPlaySample(Len: WORD; Cont: BOOLEAN);
  654.   BEGIN
  655.  
  656.     IF Len < 10 THEN EXIT;   { Too short -> Discard. It wouldn't sound anyway. }
  657.  
  658.     IF SbStereo THEN INC(Len, Len); { Twice as big a buffer if stereo mode. }
  659.     DEC(Len);                       { DMA sizes are always size - 1.        }
  660.  
  661.  
  662.     WriteNum(60, Len, $70);
  663.  
  664.     IF Sb16Detected AND (SbStereo OR Sb16Bit) THEN
  665.       Sb16StartSample(Len, Cont)
  666.     ELSE IF DoHiSpeed THEN
  667.       SbStartSampleHS(Len, Cont)
  668.     ELSE
  669.       SbStartSampleLS(Len, Cont);
  670.   END;
  671.  
  672.  
  673.  
  674.  
  675. {----------------------------------------------------------------------------}
  676. { Mixer generic routines.                                                    }
  677. {____________________________________________________________________________}
  678.  
  679. FUNCTION MixerDetect : BOOLEAN;
  680.   VAR
  681.     SaveReg : WORD;
  682.     NewReg  : WORD;
  683.   BEGIN
  684.     MixerDetect := MixerDetected;
  685.     IF NOT SbRegDetect OR MixerDetected THEN EXIT;
  686.  
  687.     SaveReg := SbReadMixerReg($22);
  688.     SbWriteMixerReg($22, 243);
  689.     NewReg  := SbReadMixerReg($22);
  690.  
  691.     IF NewReg = 243 THEN
  692.       MixerDetected := TRUE;
  693.  
  694.     SbWriteMixerReg($22, SaveReg);
  695.  
  696.     MixerDetect := MixerDetected;
  697.   END;
  698.  
  699.  
  700.  
  701.  
  702. PROCEDURE MixerSetVolume(Reg: TMixerVolume; VolLeft, VolRight: BYTE);
  703.   VAR
  704.     Addr   : BYTE;
  705.     VolMax : BYTE;
  706.   BEGIN
  707.     IF NOT MixerDetected THEN EXIT;
  708.  
  709.     IF Sb16Detected THEN Addr := Sb16Regs [Reg]
  710.                     ELSE Addr := SbProRegs[Reg];
  711.  
  712.     IF VolLeft > VolRight THEN VolMax := VolLeft
  713.                           ELSE VolMax := VolRight;
  714.  
  715.     CASE Reg OF
  716.       mvMicrophone : BEGIN
  717.                        IF Sb16Detected THEN SbWriteMixerReg(Addr, VolMax)
  718.                                        ELSE SbWriteMixerReg(Addr, VolMax SHR 5);
  719.                      END;
  720.       mvSpeaker    : BEGIN
  721.                        IF Sb16Detected THEN SbWriteMixerReg(Addr, VolMax);
  722.                      END;
  723.     ELSE
  724.  
  725.       IF Sb16Detected THEN
  726.         BEGIN
  727.           SbWriteMixerReg(Addr,     VolLeft);
  728.           SbWriteMixerReg(Addr + 1, VolRight);
  729.         END
  730.       ELSE
  731.         SbWriteMixerReg(Addr, (VolLeft  AND $F0) +
  732.                               (VolRight SHR   4));
  733.  
  734.     END;
  735.  
  736.   END;
  737.  
  738.  
  739. FUNCTION MixerGetVolume(Reg: TMixerVolume; VAR VolLeft, VolRight: BYTE) : BOOLEAN;
  740.   VAR
  741.     Addr   : BYTE;
  742.     VolMax : BYTE;
  743.   BEGIN
  744.     MixerGetVolume := FALSE;
  745.  
  746.     IF NOT MixerDetected THEN EXIT;
  747.  
  748.     IF Sb16Detected THEN Addr := Sb16Regs [Reg]
  749.                     ELSE Addr := SbProRegs[Reg];
  750.  
  751.     VolLeft  := 0;
  752.     VolRight := 0;
  753.  
  754.     MixerGetVolume := TRUE;
  755.  
  756.     CASE Reg OF
  757.       mvMicrophone : BEGIN
  758.                        IF Sb16Detected THEN VolLeft := SbReadMixerReg(Addr)
  759.                                        ELSE VolLeft := SbReadMixerReg(Addr) SHL 5;
  760.                        VolRight := VolLeft;
  761.                      END;
  762.       mvSpeaker    : BEGIN
  763.                        IF Sb16Detected THEN VolLeft := SbReadMixerReg(Addr)
  764.                                        ELSE MixerGetVolume := FALSE;
  765.                        VolRight := VolLeft;
  766.                      END;
  767.     ELSE
  768.  
  769.       IF Sb16Detected THEN
  770.         BEGIN
  771.           VolLeft  := SbReadMixerReg(Addr);
  772.           VolRight := SbReadMixerReg(Addr + 1);
  773.         END
  774.       ELSE
  775.         BEGIN
  776.           VolLeft  := SbReadMixerReg(Addr);
  777.           VolRight := VolLeft SHL 4;
  778.           VolLeft  := VolLeft AND $F0;
  779.         END;
  780.  
  781.     END;
  782.  
  783.   END;
  784.  
  785.  
  786.  
  787.  
  788. {----------------------------------------------------------------------------}
  789. { Sound Blaster Pro generic routines.                                        }
  790. {____________________________________________________________________________}
  791.  
  792. FUNCTION SbProDetect : BOOLEAN;
  793.   BEGIN
  794.     SbProDetect := SbProDetected;
  795.     IF SbProDetected THEN EXIT;
  796.  
  797.     IF NOT SbRegInited THEN SbRegInit;
  798.  
  799.     SbProDetected := SbRegDetect AND MixerDetect AND (SbVersion < $400);
  800.     SbProDetect   := SbProDetected;
  801.   END;
  802.  
  803.  
  804. PROCEDURE SbProInit;
  805.   BEGIN
  806.     IF NOT SbProDetect THEN EXIT;
  807.     SbProInited := TRUE;
  808.   END;
  809.  
  810.  
  811. PROCEDURE SbProDone;
  812.   BEGIN
  813.     SbRegDone;
  814.   END;
  815.  
  816.  
  817.  
  818.  
  819. PROCEDURE SbProSetStereo(Stereo: BOOLEAN);
  820.   VAR
  821.     i : BYTE;
  822.   BEGIN
  823.     IF NOT SbProDetected THEN EXIT;
  824.     SbStereo := Stereo;
  825.     i := SbReadMixerReg(mxrOutSetting);
  826.     SbWriteMixerReg(mxrOutSetting, (i      AND NOT mxoStereoOn) +
  827.                                    (BYTE(Stereo) * mxoStereoOn));
  828.   END;
  829.  
  830.  
  831. PROCEDURE SbProSetFilter(Filter: BOOLEAN);
  832.   VAR
  833.     i : BYTE;
  834.   BEGIN
  835.     IF NOT SbProDetected THEN EXIT;
  836.     SbFilter := Filter;
  837.     i := SbReadMixerReg(mxrOutSetting);
  838.     SbWriteMixerReg(mxrOutSetting, (i      AND NOT mxoFilterNeg) +
  839.                                    (BYTE(Filter) * mxoFilterNeg));
  840.   END;
  841.  
  842.  
  843.  
  844.  
  845. {----------------------------------------------------------------------------}
  846. { Sound Blaster 16 generic routines.                                         }
  847. {____________________________________________________________________________}
  848.  
  849. FUNCTION Sb16Detect : BOOLEAN;
  850.   BEGIN
  851.     Sb16Detect := Sb16Detected;
  852.     IF Sb16Detected THEN EXIT;
  853.  
  854.     IF NOT SbRegInited THEN SbRegInit;
  855.  
  856.     Sb16Detected := SbRegDetect AND MixerDetect AND (SbVersion >= $400);
  857.     Sb16Detect   := Sb16Detected;
  858.   END;
  859.  
  860.  
  861. PROCEDURE Sb16Init;
  862.   BEGIN
  863.     IF NOT Sb16Detect THEN EXIT;
  864.  
  865.     SbGetCopyrightString;
  866.  
  867.     Sb16Inited := TRUE;
  868.   END;
  869.  
  870.  
  871. PROCEDURE Sb16Done;
  872.   BEGIN
  873.     SbRegDone;
  874.   END;
  875.  
  876.  
  877.  
  878.  
  879. PROCEDURE Sb16StartSample(Len: WORD; Cont: BOOLEAN);
  880.   BEGIN
  881.  
  882.     IF (NOT Cont) OR (Sb16BlockSpecified <> Len){ OR TRUE }THEN
  883.       BEGIN
  884.         IF Sb16Bit THEN
  885.           SbWriteByte(SbDefTimeout, $B6)    { Set 16 bit DMA transfer command. }
  886.         ELSE
  887.           SbWriteByte(SbDefTimeout, $C6);   { Set  8 bit DMA transfer command. }
  888.         IF SbStereo THEN
  889.           SbWriteByte(SbDefTimeout, $20)    { Set stereo mode.                 }
  890.         ELSE
  891.           SbWriteByte(SbDefTimeout, $00);   { Set mono mode.                   }
  892.         SbWriteByte(SbDefTimeout, LO(Len));
  893.         SbWriteByte(SbDefTimeout, HI(Len)); { Low & high bytes of size.        }
  894.         Sb16BlockSpecified := Len;
  895.       END
  896.     ELSE
  897.       BEGIN
  898.         IF Sb16Bit THEN
  899.           SbWriteByte(SbDefTimeout, $47)    { 16 bit DMA continue command. }
  900.         ELSE
  901.           SbWriteByte(SbDefTimeout, $45);   {  8 bit DMA continue command. }
  902.       END;
  903.  
  904.   END;
  905.  
  906.  
  907.  
  908.  
  909. END.
  910.